//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/mem/span.h"
#include "sagittarius/io/io.h"
#include <string.h>

//span_hdr sh = {.n_pgs=DEFAULT_NPGS,.bytes=DEFAULT_NPGS*SG_PAGE_SIZE};
//bool span_hdr_control = false;
//t_futex span_futex = 1;
//int span_worker_wakeup = 0;

//align to pointer boundary
size_t align_tpb(size_t b) {
    if((b%SG_PTR_SIZE) == 0)
        return b;
    else
        return b+SG_PTR_SIZE-(b%SG_PTR_SIZE);
}

size_t sg_pad_marking(size_t b) {
    return b+1;
}

/*size_t sg_pad_freelist(size_t b) {
    return (b>2) ? b : 2;
}*/

size_t sg_align2(size_t b) {
    return (b%2) ? b+1 : b;
}

size_t sg_align4(size_t b) {
    if(b & 0x3)
        return (b ^ (b&0x3)) + 4;
    else
        return b;
}

void* get_pgboundary(void* addr) {
    return (void*)((intptr_t)addr & 0xFFFFFFFFFFFFF000);
}

int init_span(sg_span* s, void* addr, size_t np, uint32 bb) {
    int j;
    size_t b;
    size_t n_objs;
    uint64 bytes_avail;
    size_t sbmp;
    //int8* start;
    freenode_t* link;
    
    INITSEMA(s->sema);
    WLSEMA(s->sema);
    
    s->np = np;
    s->addr = addr;
    if(!s->addr) {
        AN(0);
        WUSEMA(s->sema);
        return 1;
    }
    //initialize small object free list
    b = sg_sc_nbytes(s->sc);
    s->ob = b;
    //pad block
    if(!bb)
        b += 4; //TODO: don't put header in bitmapped blocks
    b = sg_align4(b);//+2; //object header: aligned 2-byte word
    s->bs = b;
    
    bytes_avail = np*SG_PAGE_SIZE;
    if(!bb) {
        n_objs = bytes_avail/b;
        s->no = n_objs;
        s->sbmp = 0;
    } else {
        s->bb = bb;
        n_objs = 8*bytes_avail/(8*b + bb);
        while(1) {
            sbmp = ((bb*n_objs)%8) ? ((bb*n_objs)/8+1) : ((bb*n_objs)/8);
            if((uint64)b*n_objs + sbmp <= bytes_avail)
                break;
            --n_objs;
        }
        s->no = n_objs;
        s->sbmp = sbmp;
        memset((uint8*)addr + bytes_avail - sbmp, 0x00, sbmp);
    }
    for(j=0; j<n_objs; ++j) {
        link = (freenode_t*)((uint8*)s->addr+j*b+b-4);
        *link = (j+1);
    }
    --j;
    //adjust last link to indicate exhaustion
    link = (freenode_t*)((uint8*)s->addr+j*b+b-4);
    *link = SG_FREENODE_EXH;
    s->freenode = 0;
    s->instnode = j;
    
    WUSEMA(s->sema);
    return 0;
}

void* sg_get_smo_addr(void* start, size_t off, size_t bs) {
    return (uint8*)start + off*bs;
}

size_t sg_get_smo_header(void* obj, size_t bs) {
    size_t headeroff = bs - sizeof(freenode_t);
    return *(freenode_t*)((uint8*)obj + headeroff);
}

void span_dump_sate(int fd, sg_span* s) {
    freenode_t i;
    SG_WRITE(fd, "Span of ");
    sg_writeInt(fd, (int)s->np);
    SG_WRITE(fd, " pages:\n  Block size: ");
    sg_writeInt(fd, (int)s->bs);
    SG_WRITE(fd, "\n  No objs: ");
    sg_writeInt(fd, (int)s->no);
    if(s->sbmp) {
        SG_WRITE(fd, "\n  Bitmap size: ");
        sg_writeInt(fd, (int)s->sbmp);
    }
    SG_WRITE(fd, "\n  Size class: ");
    sg_writeInt(fd, (int)s->sc);
    SG_WRITE(fd, "\n  Queue: ");
    SG_WRITE(fd, "\n    Free node: ");
    sg_writeInt(fd, (int)s->freenode);
    SG_WRITE(fd, "\n    Tail node: ");
    sg_writeInt(fd, (int)s->instnode);
    SG_WRITE(fd, "\n");
    for(i=0; i<s->no; ++i) {
        sg_writeMemSegHex(fd, (void*)((uint8*)s->addr+i*s->bs), s->bs);
        SG_WRITE(fd, "\n");
    }
    //dump bitmap
    if(s->sbmp) {
        SG_WRITE(fd, "bitmap:\n");
        sg_writeMemSegHex(fd, (void*)((uint8*)s->addr+s->np*SG_PAGE_SIZE-s->sbmp), s->sbmp);
        SG_WRITE(fd, "\n");
    }
}